﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects;
using MetadataBasedConfigArch.Framework.Entity;

namespace MetadataBasedConfigArch.Framework
{
    public static class StaticConstraintExtension
    {
        public static IEnumerable<T> IncludeStaticConstraint<T>(this IEnumerable<T> enumerable)
        {
            if (typeof(IStaticConstrainable).IsAssignableFrom(typeof(T)))
            {
                StaticConstraintHelper.EnsureVerify<T>();
                List<string> allStaticConstaintPropertyNames = StaticConstraintHelper.GetStaticConstraintPropertyNames(typeof(T));

                if (allStaticConstaintPropertyNames.Count > 0)
                {
                    if (typeof(ObjectQuery<T>).IsAssignableFrom(enumerable.GetType()))
                    {
                        ObjectQuery<T> objectQuery = enumerable as ObjectQuery<T>;
                        StringBuilder predicateFilter = new StringBuilder();
                        List<ObjectParameter> objectParams = new List<ObjectParameter>();
                        foreach (var propertyName in allStaticConstaintPropertyNames)
                        {
                            if (predicateFilter.Length > 0)
                            {
                                predicateFilter.Append("AND ");
                            }
                            string paramName = StaticConstraintHelper.GenerateStaticConstraintParameterName(propertyName);

                            predicateFilter.AppendFormat("{0}.{1}=@{2} ", objectQuery.Name, propertyName, paramName);
                            objectParams.Add(new ObjectParameter(paramName, StaticConstraintHelper.GetStaticConstraintPropertyValue(propertyName)));
                        }
                        ObjectQuery<T> objectQuery2 = objectQuery.Where(predicateFilter.ToString());
                        objectParams.ForEach(p => objectQuery2.Parameters.Add(p));
                        return objectQuery2;
                    }
                    else
                    {
                        return enumerable.ToList().Where(t =>
                        {
                            bool result = true;
                            foreach (var propertyName in allStaticConstaintPropertyNames)
                            {
                                result &= t.GetPropertyValue(propertyName) == StaticConstraintHelper.GetStaticConstraintPropertyValue(propertyName);
                            }
                            return result;
                        });
                    }
                }
            }
            return enumerable;
        }


        public static IEnumerable<T> IncludeStaticConstraint<T>(this IEnumerable<T> enumerable, string propertyName) 
        {
            if (typeof(IStaticConstrainable).IsAssignableFrom(typeof(T)))
            {
                StaticConstraintHelper.EnsureVerify<T>();
                if (!StaticConstraintHelper.GetStaticConstraintPropertyNames(typeof(T)).Contains(propertyName))
                {
                    throw new InvalidOperationException(string.Format("The property:{1} of type:{0} can't be static constrainable", typeof(T).FullName, propertyName));
                }

                if (typeof(ObjectQuery<T>).IsAssignableFrom(enumerable.GetType()))
                {
                    string paramName = StaticConstraintHelper.GenerateStaticConstraintParameterName(propertyName);

                    ObjectQuery<T> objectQuery = (ObjectQuery<T>)enumerable;
                    StringBuilder predicateFilter = new StringBuilder();
                    predicateFilter.AppendFormat("{0}.{1}=@{2} ", objectQuery.Name, propertyName, paramName);
                    ObjectQuery<T> objectQuery2 = objectQuery.Where(predicateFilter.ToString());
                    objectQuery2.Parameters.Add(new ObjectParameter(paramName, StaticConstraintHelper.GetStaticConstraintPropertyValue(propertyName)));
                    return objectQuery2;
                }
                else
                {
                    return enumerable.ToList().Where(t => t.GetPropertyValue(propertyName) == StaticConstraintHelper.GetStaticConstraintPropertyValue(propertyName));
                }
            }
            return enumerable;
        }

        /// <summary>
        /// Load the configuration information of IConfigurable entity.
        /// Should used after linq query.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="objectQuery"></param>
        /// <returns></returns>
        public static IEnumerable<T> LoadExtendProperties<T>(this IEnumerable<T> objectQuery, IMetadataBasedEntityProvider entityProvider)
            where T : MetadataBasedConfigArch.Framework.IConfigurable
        {
            List<T> allEntities = objectQuery.ToList();
            allEntities.ForEach(e => e.LoadConfigurableInfos(entityProvider));
            return allEntities;
        }
    }
}
